Code copied from Ferroptosis_heatmaps.R and fixed to run
within this repo.
library(reshape2)
library(ggplot2)
library(scales)
library(pheatmap)
SAVEPLOTS <- FALSE
load("../data/merged_countdata.RData") # object countdata
Ferr <- read.delim("../data/KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
ferr_genes <- Ferr$GeneName # ferroptosis genes from KEGG (2018)
load("../data/RLD_SC-1,7,10_0,3,8d_20180701.RData") # object rld
Loading required package: DESeq2
Loading required package: S4Vectors
Warning: package ‘S4Vectors’ was built under R version 4.3.2
Loading required package: stats4
Loading required package: BiocGenerics
Attaching package: ‘BiocGenerics’
The following objects are masked from ‘package:stats’:
IQR, mad, sd, var, xtabs
The following objects are masked from ‘package:base’:
anyDuplicated, aperm, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int, Position,
rank, rbind, Reduce, rownames, sapply, setdiff, sort, table, tapply, union, unique, unsplit, which.max, which.min
Attaching package: ‘S4Vectors’
The following object is masked from ‘package:utils’:
findMatches
The following objects are masked from ‘package:base’:
expand.grid, I, unname
Loading required package: IRanges
Loading required package: GenomicRanges
Loading required package: GenomeInfoDb
Warning: package ‘GenomeInfoDb’ was built under R version 4.3.2
Loading required package: SummarizedExperiment
Warning: package ‘SummarizedExperiment’ was built under R version 4.3.2
Loading required package: MatrixGenerics
Loading required package: matrixStats
Attaching package: ‘MatrixGenerics’
The following objects are masked from ‘package:matrixStats’:
colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse, colCounts, colCummaxs, colCummins, colCumprods, colCumsums, colDiffs,
colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs, colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats, colProds,
colQuantiles, colRanges, colRanks, colSdDiffs, colSds, colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
colWeightedMeans, colWeightedMedians, colWeightedSds, colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet, rowCollapse,
rowCounts, rowCummaxs, rowCummins, rowCumprods, rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps, rowMadDiffs, rowMads,
rowMaxs, rowMeans2, rowMedians, rowMins, rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks, rowSdDiffs, rowSds, rowSums2,
rowTabulates, rowVarDiffs, rowVars, rowWeightedMads, rowWeightedMeans, rowWeightedMedians, rowWeightedSds, rowWeightedVars
Loading required package: Biobase
Welcome to Bioconductor
Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see 'citation("Biobase")', and for
packages 'citation("pkgname")'.
Attaching package: ‘Biobase’
The following object is masked from ‘package:MatrixGenerics’:
rowMedians
The following objects are masked from ‘package:matrixStats’:
anyMissing, rowMedians
Another ferroptosis gene list from Wikipathways/GSEA: https://www.gsea-msigdb.org/gsea/msigdb/cards/WP_FERROPTOSIS.html
File downloaded 2024-01-14 and saved in
../data/WP_FERROPTOSIS.v2023.2.Hs.tsv
ferr_genes_wikipw_raw <- read.table('../data/WP_FERROPTOSIS.v2023.2.Hs.tsv', sep="\t", row.names=1)
ferr_genes_wikipw <- ferr_genes_wikipw_raw["GENE_SYMBOLS",]
ferr_genes_wikipw <- unlist(strsplit(ferr_genes_wikipw,","))
ferr_genes_wikipw <- ferr_genes_wikipw[ferr_genes_wikipw!=""]
Unclear why these genes should be excluded.
excl_genes <- c("ALOX15", "ACSL1", "ACSL3",
"ACSL5", "ACSL6", "TP53", "TF",
"CP", "MAP1LC3A", "MAP1LC3C",
"CYBB")
Define which gene set to use for all plots
object is ferrgenes_4plots. Setting to first item in
list will use newer and larger list of genes from wikipathways; setting
to item 2 will make output match previously generated plots.
ferr_genes_4plots <- list(ferr_genes_wikipw,ferr_genes)[[1]]
Process raw count data
Ferr <- Ferr[rowSums(is.na(Ferr)) == 0, ]
countdata_Fer <- countdata[ferr_genes_4plots,] # select genes here
Fer_samples = colsplit(colnames(countdata_Fer), pattern = "_", names = c("Population", "Time", "Replicate"))
Fer_plot = cbind(Fer_samples, t(countdata_Fer))
Fer_melt = melt(data = Fer_plot, id.vars = c("Population", "Time", "Replicate"), measure.vars = ferr_genes_4plots) # select genes here
Fer_dat = Rmisc::summarySE(Fer_melt, measurevar = "value", groupvars = c("Population", "Time", "variable"))
Fer_dat$Time = as.numeric(gsub("[^[:digit:]]","",Fer_dat$Time))
Plot change over time of BRAFi for each subclone
Changed plotting from linear to log2 scale for better visualization
of changes.
Fer_ggploted <- ggplot(Fer_dat, aes(x=Time, y=log2(value), group = interaction(variable, Population))) +
geom_line(linewidth=1.5, aes(color = Population)) +
geom_point(size = 1.5, aes(color = Population)) + facet_wrap(~variable, ncol = 5, scales = "free") +
geom_errorbar(aes(ymin=log2(value-sd), ymax=log2(value+sd), color = Population), width=.2, linewidth=1.5) +
theme_bw() + xlab("Time (days)") + ylab("Gene Counts") +
ggtitle("Ferroptosis gene signature") +
theme(legend.text = element_text(size = 10), legend.position = "right",
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
axis.title=element_text(size=12,face="bold"))
Fer_ggploted

if(SAVEPLOTS) ggsave("FerroptosisGeneSignature_rawCounts_SKMEL5sublines+treatment.pdf", width = 20, height = 25)
NOTE: This will overwrite objects with new data
Normalized data from DESeq2
normdata_Fer <- as.data.frame(assay(rld))[ferr_genes_4plots,]
Fer_match <- colsplit(colnames(normdata_Fer), pattern = "_", names = c("Population", "Time", "Replicate"))
Fer_plot <- cbind(Fer_match, t(normdata_Fer))
Fer_melt <- melt(data = Fer_plot, id.vars = c("Population", "Time", "Replicate"), measure.vars = unique(colnames(Fer_plot))[4:42])
Fer_dat <- Rmisc::summarySE(Fer_melt, measurevar = "value", groupvars = c("Population", "Time", "variable"))
Fer_dat$Time <- as.numeric(gsub("[^[:digit:]]","",Fer_dat$Time))
# normdata_Fer
samples <- c("SC01_day0", "SC01_day3", "SC01_day8", "SC07_day0", "SC07_day3", "SC07_day8", "SC10_day0", "SC10_day3", "SC10_day8")
test <- normdata_Fer
test1 <- sapply(samples, function(x) rowMeans(test[, grep(x, colnames(test))]))
test2 <- as.data.frame(test1[complete.cases(test1),])
####
# Plot pheatmap (z-score) of gene list
test3 <- test2
test3$Gene <- rownames(test2)
test3_sub <- subset(test3, !Gene %in% excl_genes)
test3_sub <- test3_sub[,1:9]
pheatmap(test3_sub,cluster_cols=FALSE, cluster_rows = F, scale = "row")

test4_special <- subset(test3, Gene %in% ferr_genes_4plots)
test4_special <- test4_special[,1:9]
pheatmap(test4_special,cluster_cols=FALSE, cluster_rows = F, scale = "row")

NA
NA
allFC <- function(DEProc,startcol,endcol){
GE_fold = DEProc[,-c(startcol:endcol)]
colvec = colnames(DEProc)[startcol:endcol]
#Last index is a self comparison and is removed
for(k in 1:(length(colvec)-1)){
#Start with column that is 1 away from index
for(j in (k+1):length(colvec)){
compnam = paste0(colvec[j],"/",colvec[k])
#Loop through each gene/row
for(i in 1:nrow(DEProc)){
f = DEProc[i,colvec[j]]
h = DEProc[i,colvec[k]]
GE_fold[i, compnam] = log2(f/h)
#Capture upregulation and down regulation
# if(f>h){
# # GE_fold[i,compnam] = 2^(f-h)
# GE_fold[i,compnam] = log2(f/h)
# }else{
# # GE_fold[i,compnam] = -2^(h-f)
# GE_fold[i,compnam] = log2(f/h)
# }
#
}
}
}
return(GE_fold)
}
GE_fold <- allFC(test2, 1,9)
ImpRat = c("SC01_day3/SC01_day0", "SC01_day8/SC01_day0",
"SC07_day3/SC07_day0", "SC07_day8/SC07_day0",
"SC10_day3/SC10_day0", "SC10_day8/SC10_day0")
Imp_fold <- GE_fold[,ImpRat]
Ferr <- read.delim("../data/KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
Ferr_fold <- Imp_fold[rownames(Imp_fold) %in% ferr_genes_4plots,]
Ferr_fold$Gene <- rownames(Ferr_fold)
# clean column names
colnames(Ferr_fold) <- gsub("/SC[01][170]_day0","",colnames(Ferr_fold))
Ferr_fold_melt <- melt(Ferr_fold, id.vars = "Gene")
Ferr_fold_melt$Gene <- factor(Ferr_fold_melt$Gene, levels = rev(ferr_genes_4plots))
Ferr_fold_melt <- subset(Ferr_fold_melt, !Gene %in% excl_genes)
myplot <- ggplot(Ferr_fold_melt, aes(variable, Gene, fill = value)) +
geom_tile(color = "black") + theme_bw() +
scale_fill_gradientn(
colors=c("blue","white","red","red4"),
values=rescale(c(min(Ferr_fold_melt$value), 0, max(Ferr_fold_melt$value)/2, max(Ferr_fold_melt$value))),
limits=c(min(Ferr_fold_melt$value),max(Ferr_fold_melt$value)),
name = "Log2 Fold Change"
) + ylab("") + xlab("") +
theme(axis.text=element_text(size=10),
axis.text.x=element_text(angle = 90, hjust = 0),
axis.title=element_text(size=12),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
myplot

if(SAVEPLOTS) ggsave("Ferroptosis_FCHM_selected_wide.pdf", width = 8, height = 8)

Using ComplexHeatmap()
if (!requireNamespace("BiocManager", quietly=TRUE))
install.packages("BiocManager")
BiocManager::install("ComplexHeatmap")
LS0tCnRpdGxlOiAiQ29udmVydGluZyBGZXJyb3B0b3NpcyBoZWF0bWFwIGNvZGUgdG8gbm90ZWJvb2siCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IERhcnJlbiBUeXNvbgpkYXRlOiAyMDI0LTAxLTE0Ci0tLQoKQ29kZSBjb3BpZWQgZnJvbSBgRmVycm9wdG9zaXNfaGVhdG1hcHMuUmAgYW5kIGZpeGVkIHRvIHJ1biB3aXRoaW4gdGhpcyByZXBvLgoKCmBgYHtyfQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KGdwbG90cykgIyBuZWVkZWQgZm9yIGNvbG9yIHBhbGV0dGVzLCByZWRibHVlKCksIGNvbG9yUmFtcFBhbGV0dGUoKQpgYGAKCmBgYHtyfQpTQVZFUExPVFMgPC0gRkFMU0UKYGBgCgoKYGBge3IgTG9hZCBkYXRhfQpsb2FkKCIuLi9kYXRhL21lcmdlZF9jb3VudGRhdGEuUkRhdGEiKSAjIG9iamVjdCBjb3VudGRhdGEKRmVyciA8LSByZWFkLmRlbGltKCIuLi9kYXRhL0tFR0dGZXJyb3B0b3Npc19oc2EwNDIxNl8wNi0yNS0xOC50eHQiLCBoZWFkZXI9VCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmZlcnJfZ2VuZXMgPC0gRmVyciRHZW5lTmFtZSAgIyBmZXJyb3B0b3NpcyBnZW5lcyBmcm9tIEtFR0cgKDIwMTgpCmxvYWQoIi4uL2RhdGEvUkxEX1NDLTEsNywxMF8wLDMsOGRfMjAxODA3MDEuUkRhdGEiKSAgIyBvYmplY3QgcmxkCmBgYAoKQW5vdGhlciBmZXJyb3B0b3NpcyBnZW5lIGxpc3QgZnJvbSBXaWtpcGF0aHdheXMvR1NFQTogaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2NhcmRzL1dQX0ZFUlJPUFRPU0lTLmh0bWwKRmlsZSBkb3dubG9hZGVkIDIwMjQtMDEtMTQgYW5kIHNhdmVkIGluIGAuLi9kYXRhL1dQX0ZFUlJPUFRPU0lTLnYyMDIzLjIuSHMudHN2YAoKYGBge3J9CmZlcnJfZ2VuZXNfd2lraXB3X3JhdyA8LSByZWFkLnRhYmxlKCcuLi9kYXRhL1dQX0ZFUlJPUFRPU0lTLnYyMDIzLjIuSHMudHN2Jywgc2VwPSJcdCIsIHJvdy5uYW1lcz0xKQpmZXJyX2dlbmVzX3dpa2lwdyA8LSBmZXJyX2dlbmVzX3dpa2lwd19yYXdbIkdFTkVfU1lNQk9MUyIsXQpmZXJyX2dlbmVzX3dpa2lwdyA8LSB1bmxpc3Qoc3Ryc3BsaXQoZmVycl9nZW5lc193aWtpcHcsIiwiKSkKZmVycl9nZW5lc193aWtpcHcgPC0gZmVycl9nZW5lc193aWtpcHdbZmVycl9nZW5lc193aWtpcHchPSIiXQpgYGAKCgpVbmNsZWFyIHdoeSB0aGVzZSBnZW5lcyBzaG91bGQgYmUgZXhjbHVkZWQuCmBgYHtyfQpleGNsX2dlbmVzIDwtIGMoIkFMT1gxNSIsICJBQ1NMMSIsICJBQ1NMMyIsCiAgICAgICAgICAgICAgICAiQUNTTDUiLCAiQUNTTDYiLCAiVFA1MyIsICJURiIsCiAgICAgICAgICAgICAgICAiQ1AiLCAiTUFQMUxDM0EiLCAiTUFQMUxDM0MiLAogICAgICAgICAgICAgICAgIkNZQkIiKQpgYGAKCiMjIyBEZWZpbmUgd2hpY2ggZ2VuZSBzZXQgdG8gdXNlIGZvciBhbGwgcGxvdHMKb2JqZWN0IGlzIGBmZXJyZ2VuZXNfNHBsb3RzYC4gU2V0dGluZyB0byBmaXJzdCBpdGVtIGluIGxpc3Qgd2lsbCB1c2UgbmV3ZXIgYW5kIGxhcmdlciBsaXN0IG9mIGdlbmVzIGZyb20gd2lraXBhdGh3YXlzOyBzZXR0aW5nIHRvIGl0ZW0gMiB3aWxsIG1ha2Ugb3V0cHV0IG1hdGNoIHByZXZpb3VzbHkgZ2VuZXJhdGVkIHBsb3RzLgpgYGB7cn0KZmVycl9nZW5lc180cGxvdHMgPC0gbGlzdChmZXJyX2dlbmVzX3dpa2lwdyxmZXJyX2dlbmVzKVtbMV1dCmBgYAoKCiMjIyBQcm9jZXNzIHJhdyBjb3VudCBkYXRhCmBgYHtyfQpGZXJyIDwtIEZlcnJbcm93U3Vtcyhpcy5uYShGZXJyKSkgPT0gMCwgXQoKY291bnRkYXRhX0ZlciA8LSBjb3VudGRhdGFbZmVycl9nZW5lc180cGxvdHMsXSAgIyBzZWxlY3QgZ2VuZXMgaGVyZQoKRmVyX3NhbXBsZXMgPSBjb2xzcGxpdChjb2xuYW1lcyhjb3VudGRhdGFfRmVyKSwgcGF0dGVybiA9ICJfIiwgbmFtZXMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIikpCkZlcl9wbG90ID0gY2JpbmQoRmVyX3NhbXBsZXMsIHQoY291bnRkYXRhX0ZlcikpCkZlcl9tZWx0ID0gbWVsdChkYXRhID0gRmVyX3Bsb3QsIGlkLnZhcnMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIiksIG1lYXN1cmUudmFycyA9IGZlcnJfZ2VuZXNfNHBsb3RzKSAgIyBzZWxlY3QgZ2VuZXMgaGVyZQpGZXJfZGF0ID0gUm1pc2M6OnN1bW1hcnlTRShGZXJfbWVsdCwgbWVhc3VyZXZhciA9ICJ2YWx1ZSIsIGdyb3VwdmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJ2YXJpYWJsZSIpKQpGZXJfZGF0JFRpbWUgPSBhcy5udW1lcmljKGdzdWIoIlteWzpkaWdpdDpdXSIsIiIsRmVyX2RhdCRUaW1lKSkKYGBgCgojIyMgUGxvdCBjaGFuZ2Ugb3ZlciB0aW1lIG9mIEJSQUZpIGZvciBlYWNoIHN1YmNsb25lCkNoYW5nZWQgcGxvdHRpbmcgZnJvbSBsaW5lYXIgdG8gbG9nMiBzY2FsZSBmb3IgYmV0dGVyIHZpc3VhbGl6YXRpb24gb2YgY2hhbmdlcy4KYGBge3IgZmlnLmhlaWdodD0yNSwgZmlnLndpZHRoPTIwfQpGZXJfZ2dwbG90ZWQgPC0gZ2dwbG90KEZlcl9kYXQsIGFlcyh4PVRpbWUsIHk9bG9nMih2YWx1ZSksIGdyb3VwID0gaW50ZXJhY3Rpb24odmFyaWFibGUsIFBvcHVsYXRpb24pKSkgKyAKICBnZW9tX2xpbmUobGluZXdpZHRoPTEuNSwgYWVzKGNvbG9yID0gUG9wdWxhdGlvbikpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBhZXMoY29sb3IgPSBQb3B1bGF0aW9uKSkgKyBmYWNldF93cmFwKH52YXJpYWJsZSwgbmNvbCA9IDUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bG9nMih2YWx1ZS1zZCksIHltYXg9bG9nMih2YWx1ZStzZCksIGNvbG9yID0gUG9wdWxhdGlvbiksIHdpZHRoPS4yLCBsaW5ld2lkdGg9MS41KSArCiAgdGhlbWVfYncoKSArIHhsYWIoIlRpbWUgKGRheXMpIikgKyB5bGFiKCJHZW5lIENvdW50cyIpICsKICBnZ3RpdGxlKCJGZXJyb3B0b3NpcyBnZW5lIHNpZ25hdHVyZSIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSkKCkZlcl9nZ3Bsb3RlZAoKaWYoU0FWRVBMT1RTKSBnZ3NhdmUoIkZlcnJvcHRvc2lzR2VuZVNpZ25hdHVyZV9yYXdDb3VudHNfU0tNRUw1c3VibGluZXMrdHJlYXRtZW50LnBkZiIsIHdpZHRoID0gMjAsIGhlaWdodCA9IDI1KQpgYGAKCgojIyMgTk9URTogVGhpcyB3aWxsIG92ZXJ3cml0ZSBvYmplY3RzIHdpdGggbmV3IGRhdGEKTm9ybWFsaXplZCBkYXRhIGZyb20gREVTZXEyCmBgYHtyfQpub3JtZGF0YV9GZXIgPC0gYXMuZGF0YS5mcmFtZShhc3NheShybGQpKVtmZXJyX2dlbmVzXzRwbG90cyxdCgpGZXJfbWF0Y2ggPC0gY29sc3BsaXQoY29sbmFtZXMobm9ybWRhdGFfRmVyKSwgcGF0dGVybiA9ICJfIiwgbmFtZXMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIikpCkZlcl9wbG90IDwtIGNiaW5kKEZlcl9tYXRjaCwgdChub3JtZGF0YV9GZXIpKQpGZXJfbWVsdCA8LSBtZWx0KGRhdGEgPSBGZXJfcGxvdCwgaWQudmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJSZXBsaWNhdGUiKSwgbWVhc3VyZS52YXJzID0gdW5pcXVlKGNvbG5hbWVzKEZlcl9wbG90KSlbNDo0Ml0pCkZlcl9kYXQgPC0gUm1pc2M6OnN1bW1hcnlTRShGZXJfbWVsdCwgbWVhc3VyZXZhciA9ICJ2YWx1ZSIsIGdyb3VwdmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJ2YXJpYWJsZSIpKQpGZXJfZGF0JFRpbWUgPC0gYXMubnVtZXJpYyhnc3ViKCJbXls6ZGlnaXQ6XV0iLCIiLEZlcl9kYXQkVGltZSkpCmBgYAoKCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9CiMgbm9ybWRhdGFfRmVyCnNhbXBsZXMgPC0gYygiU0MwMV9kYXkwIiwgIlNDMDFfZGF5MyIsICJTQzAxX2RheTgiLCAiU0MwN19kYXkwIiwgIlNDMDdfZGF5MyIsICJTQzA3X2RheTgiLCAiU0MxMF9kYXkwIiwgIlNDMTBfZGF5MyIsICJTQzEwX2RheTgiKQp0ZXN0IDwtIG5vcm1kYXRhX0Zlcgp0ZXN0MSA8LSBzYXBwbHkoc2FtcGxlcywgZnVuY3Rpb24oeCkgcm93TWVhbnModGVzdFssIGdyZXAoeCwgY29sbmFtZXModGVzdCkpXSkpCnRlc3QyIDwtIGFzLmRhdGEuZnJhbWUodGVzdDFbY29tcGxldGUuY2FzZXModGVzdDEpLF0pCgojIyMjCiMgUGxvdCBwaGVhdG1hcCAoei1zY29yZSkgb2YgZ2VuZSBsaXN0IAp0ZXN0MyA8LSB0ZXN0Mgp0ZXN0MyRHZW5lIDwtIHJvd25hbWVzKHRlc3QyKQp0ZXN0M19zdWIgPC0gc3Vic2V0KHRlc3QzLCAhR2VuZSAlaW4lIGV4Y2xfZ2VuZXMpCnRlc3QzX3N1YiA8LSB0ZXN0M19zdWJbLDE6OV0KcGhlYXRtYXAodGVzdDNfc3ViLGNsdXN0ZXJfY29scz1GQUxTRSwgY2x1c3Rlcl9yb3dzID0gRiwgc2NhbGUgPSAicm93IikKCnRlc3Q0X3NwZWNpYWwgPC0gc3Vic2V0KHRlc3QzLCBHZW5lICVpbiUgZmVycl9nZW5lc180cGxvdHMpCnRlc3Q0X3NwZWNpYWwgPC0gdGVzdDRfc3BlY2lhbFssMTo5XQpwaGVhdG1hcCh0ZXN0NF9zcGVjaWFsLGNsdXN0ZXJfY29scz1GQUxTRSwgY2x1c3Rlcl9yb3dzID0gRiwgc2NhbGUgPSAicm93IikKCgpgYGAKCmBgYHtyfQoKYWxsRkMgPC0gZnVuY3Rpb24oREVQcm9jLHN0YXJ0Y29sLGVuZGNvbCl7IAogIEdFX2ZvbGQgPSBERVByb2NbLC1jKHN0YXJ0Y29sOmVuZGNvbCldCiAgY29sdmVjID0gY29sbmFtZXMoREVQcm9jKVtzdGFydGNvbDplbmRjb2xdCiAgCiAgI0xhc3QgaW5kZXggaXMgYSBzZWxmIGNvbXBhcmlzb24gYW5kIGlzIHJlbW92ZWQKICBmb3IoayBpbiAxOihsZW5ndGgoY29sdmVjKS0xKSl7CiAgICAjU3RhcnQgd2l0aCBjb2x1bW4gdGhhdCBpcyAxIGF3YXkgZnJvbSBpbmRleCAKICAgIGZvcihqIGluIChrKzEpOmxlbmd0aChjb2x2ZWMpKXsKICAgICAgY29tcG5hbSA9IHBhc3RlMChjb2x2ZWNbal0sIi8iLGNvbHZlY1trXSkKICAgICAgI0xvb3AgdGhyb3VnaCBlYWNoIGdlbmUvcm93ICAKICAgICAgZm9yKGkgaW4gMTpucm93KERFUHJvYykpewogICAgICAgIGYgPSBERVByb2NbaSxjb2x2ZWNbal1dCiAgICAgICAgaCA9IERFUHJvY1tpLGNvbHZlY1trXV0KICAgICAgICBHRV9mb2xkW2ksIGNvbXBuYW1dID0gbG9nMihmL2gpCgogICAgICAgICNDYXB0dXJlIHVwcmVndWxhdGlvbiBhbmQgZG93biByZWd1bGF0aW9uCiAgICAgICAgIyBpZihmPmgpewogICAgICAgICMgICAjIEdFX2ZvbGRbaSxjb21wbmFtXSA9IDJeKGYtaCkKICAgICAgICAjICAgR0VfZm9sZFtpLGNvbXBuYW1dID0gbG9nMihmL2gpCiAgICAgICAgIyB9ZWxzZXsKICAgICAgICAjICAgIyBHRV9mb2xkW2ksY29tcG5hbV0gPSAtMl4oaC1mKQogICAgICAgICMgICBHRV9mb2xkW2ksY29tcG5hbV0gPSBsb2cyKGYvaCkKICAgICAgIyAgIH0KICAgICAgIyAgIAogICAgIH0KICAgIH0KICB9CiAgCiAgcmV0dXJuKEdFX2ZvbGQpCiAgCn0KCkdFX2ZvbGQgPC0gYWxsRkModGVzdDIsIDEsOSkKSW1wUmF0ID0gYygiU0MwMV9kYXkzL1NDMDFfZGF5MCIsICJTQzAxX2RheTgvU0MwMV9kYXkwIiwgCiAgICAgICAgICAgIlNDMDdfZGF5My9TQzA3X2RheTAiLCAiU0MwN19kYXk4L1NDMDdfZGF5MCIsIAogICAgICAgICAgICJTQzEwX2RheTMvU0MxMF9kYXkwIiwgIlNDMTBfZGF5OC9TQzEwX2RheTAiKQpJbXBfZm9sZCA8LSBHRV9mb2xkWyxJbXBSYXRdCgpGZXJyIDwtIHJlYWQuZGVsaW0oIi4uL2RhdGEvS0VHR0ZlcnJvcHRvc2lzX2hzYTA0MjE2XzA2LTI1LTE4LnR4dCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzID0gRikKRmVycl9mb2xkIDwtIEltcF9mb2xkW3Jvd25hbWVzKEltcF9mb2xkKSAlaW4lIGZlcnJfZ2VuZXNfNHBsb3RzLF0KCkZlcnJfZm9sZCRHZW5lIDwtIHJvd25hbWVzKEZlcnJfZm9sZCkKCiMgY2xlYW4gY29sdW1uIG5hbWVzCmNvbG5hbWVzKEZlcnJfZm9sZCkgPC0gZ3N1YigiL1NDWzAxXVsxNzBdX2RheTAiLCIiLGNvbG5hbWVzKEZlcnJfZm9sZCkpCmBgYAoKIyMjIApgYGB7cn0KRmVycl9mb2xkX21lbHQgPC0gbWVsdChGZXJyX2ZvbGQsIGlkLnZhcnMgPSAiR2VuZSIpCkZlcnJfZm9sZF9tZWx0JEdlbmUgPC0gZmFjdG9yKEZlcnJfZm9sZF9tZWx0JEdlbmUsIGxldmVscyA9IHJldihmZXJyX2dlbmVzXzRwbG90cykpCgpGZXJyX2ZvbGRfbWVsdCA8LSBzdWJzZXQoRmVycl9mb2xkX21lbHQsICFHZW5lICVpbiUgZXhjbF9nZW5lcykKYGBgCgoKYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OH0KbXlwbG90IDwtIGdncGxvdChGZXJyX2ZvbGRfbWVsdCwgYWVzKHZhcmlhYmxlLCBHZW5lLCBmaWxsID0gdmFsdWUpKSArIAogIGdlb21fdGlsZShjb2xvciA9ICJibGFjayIpICsgdGhlbWVfYncoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oCiAgICBjb2xvcnM9YygiYmx1ZSIsIndoaXRlIiwicmVkIiwicmVkNCIpLAogICAgdmFsdWVzPXJlc2NhbGUoYyhtaW4oRmVycl9mb2xkX21lbHQkdmFsdWUpLCAwLCBtYXgoRmVycl9mb2xkX21lbHQkdmFsdWUpLzIsIG1heChGZXJyX2ZvbGRfbWVsdCR2YWx1ZSkpKSwKICAgIGxpbWl0cz1jKG1pbihGZXJyX2ZvbGRfbWVsdCR2YWx1ZSksbWF4KEZlcnJfZm9sZF9tZWx0JHZhbHVlKSksCiAgICBuYW1lID0gIkxvZzIgRm9sZCBDaGFuZ2UiCiAgKSArIHlsYWIoIiIpICsgeGxhYigiIikgKyAKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDApLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCgpteXBsb3QKaWYoU0FWRVBMT1RTKSBnZ3NhdmUoIkZlcnJvcHRvc2lzX0ZDSE1fc2VsZWN0ZWRfd2lkZS5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCmBgYAoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9CnBhcihtYXI9Yyg4LDUsMSwxKSwgb21hPWMoMywxLDAsMCkpCmR0cCA8LSBhcy5tYXRyaXgoRmVycl9mb2xkWywxOjZdKQpkdHAgPC0gZHRwWyFyb3duYW1lcyhkdHApICVpbiUgZXhjbF9nZW5lcyxdCmNvbG5hbWVzKGR0cCkgPC0gZ3N1YigiZGF5IiwiRCIsY29sbmFtZXMoZHRwKSkgCm15Y29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiYmx1ZSIsIndoaXRlIiwicmVkIikpKDUwKQoKZ3Bsb3RzOjpoZWF0bWFwLjIoZHRwLCBkZW5kcm9ncmFtPSJub25lIiwgc2NhbGU9Im5vbmUiLCBSb3d2PUZBTFNFLCBDb2x2PUZBTFNFLCB0cmFjZT0ibm9uZSIsIHRyYWNlY29sPU5BLAogICAgICAgICAgICAgICAgICBjb2w9bXljb2xvcnMsIHNydENvbD0wLCBhZGpDb2w9MC41LCBjb2xzZXA9YygyLDQpLCBvZmZzZXRSb3c9LTM1LCBhZGpSb3c9YygxLCAwLjUpLAogICAgICAgICAgICAgICAgICBjZXhDb2wgPSAwLjIgKyAwLjc1L2xvZzEwKG5jb2woZHRwKSksIGNleC5sYWI9MC41LAogICAgICAgICAgICAgICAgICBrZXlzaXplPTEsIGtleS50aXRsZT1OQSwga2V5LnlsYWI9TkEsIGtleS54bGFiPSJMb2cyIGZvbGQgY2hhbmdlIiwga2V5LnBhcj1saXN0KG1hcj1jKDYsMyw1LDEpKSkKYGBgCgoKIyMgVXNpbmcgYENvbXBsZXhIZWF0bWFwKClgCmBgYHtyfQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseT1UUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKQmlvY01hbmFnZXI6Omluc3RhbGwoIkNvbXBsZXhIZWF0bWFwIikKYGBgCgo=